import ctypes
import inspect
import json
import socket
import threading
import time
import traceback

from pond import Pond, PooledObjectFactory, PooledObject

from com.util.loggingUtil import Logs
from com.util.tcp import pbMessageCompileHelper

Log = Logs('Tcp')

commonReq = {
    "userId": "111",
    "token": "111",
    "termType": 2,
    "deviceNo": "111"
}


def _async_raise(tid, exctype):
    """
    # 根据tid停止线程
    raises the exception, performs cleanup if needed
    """

    tid = ctypes.c_long(tid)

    if not inspect.isclass(exctype):
        exctype = type(exctype)

    res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))

    if res == 0:

        raise ValueError("invalid thread id")

    elif res != 1:

        # """if it returns a number greater than one, you're in trouble,

        # and you should call it again with exc=NULL to revert the effect"""

        ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None)

        raise SystemError("PyThreadState_SetAsyncExc failed")


def stop_thread(thread):
    _async_raise(thread.ident, SystemExit)


class TcpClient(object):
    state = True

    def receiveData(self, functionId, sequeueNo):
        limitCount = 0
        res = {}
        key = f"{functionId}-{sequeueNo}"
        while True:
            resDict: dict = self.tcpRes
            if key in resDict.keys():
                res = json.loads(resDict.get(key))
                resDict.pop(key, None)
                break
            else:
                time.sleep(0.5)
                limitCount = limitCount + 1
                # 防止接受不到数据死循环
                if limitCount > 15 * 2:
                    break
        return res

    def sendTcpData(self, functionId, sequeueNo, data):
        with self._vLock:
            Log.info(f"id[{id(self)}]-sendTcpData,{functionId}-{sequeueNo}-{data}")
            bodyMesasge = pbMessageCompileHelper.mockPbMessageBydata(functionId, data)
            sendMessage = pbMessageCompileHelper.mockMessageByBodyMessage(functionId, sequeueNo, bodyMesasge)
            flag = self.soketClient.sendall(sendMessage)

    def __init__(self):
        # test
        # address = ('push-test.wogoo.com', 30000)
        address = ('push.wogoo.com', 9000)
        self.soketClient = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.soketClient.connect(address)
        # 业务心跳
        self.socket_keepAlive()
        # 开启接受
        self.socket_onRecev()
        # 接受返回值对象
        self.tcpRes = {}
        # 枷锁
        self._vLock = threading.Lock()

    def socket_onRecev(self):
        """
        子线程开启接受数据
        :return:
        """
        soketClient = self.soketClient

        def recev_trhead(soketClient=soketClient):
            temp = bytes()
            isRecvHeader = True

            while True:
                # 接受数据  防止粘包处理逻辑
                temp += soketClient.recv(1024)

                startIndex = temp.find(b'WG')

                if startIndex >= 0 and isRecvHeader:
                    # 截断数据
                    temp = temp[startIndex:]
                    # 不足补足
                    if temp.__len__() < 11:
                        continue

                    # 解析头数据
                    data = pbMessageCompileHelper.decodeHeader(temp[0: 11])
                    # 解析完后截断数据
                    isRecvHeader = False
                    temp = temp[data['lindex']:]
                    bodyLength = data['bodylength']

                # 解析数据体
                if not isRecvHeader:
                    try:
                        if temp.__len__() < bodyLength:
                            continue

                        Log.info(f"id[{id(self)}]-recv-header:{data}")
                        # 解析body体数据
                        self.decodeBody(temp[:bodyLength], data['functionId'], data['sequeueNo'])
                        # 解析完成 截断temp 丢弃已解析部分
                        temp = temp[bodyLength:]
                        isRecvHeader = True
                    except:
                        Log.error(traceback.format_exc())
                        isRecvHeader = True

        self.t1 = threading.Thread(target=recev_trhead)
        self.t1.start()

    def socket_keepAlive(self):
        """
        业务心跳
        :param socketClient:
        :return:
        """
        socketClient = self.soketClient

        def send_trhead(socketClient=socketClient):
            while True:
                messageDict = {
                    "commonReq": commonReq
                }
                pbMessage = pbMessageCompileHelper.mockPbMessageBydata("101", messageDict)
                message: bytes = pbMessageCompileHelper.mockMessageByBodyMessage("101", '1', pbMessage)
                socketClient.send(message)
                time.sleep(30)

        self.t2 = threading.Thread(target=send_trhead)
        self.t2.start()

    def decodeBody(self, bsBody=None, functionId='101', sequeueNo='1'):
        # 获取映射
        jsonRes = pbMessageCompileHelper.decodeBody(bsBody, functionId)
        # Log.info(f"id[{id(self)}]-recevBody{functionId}-{sequeueNo}:{jsonRes}")
        self.tcpRes.__setitem__(f"{functionId}-{sequeueNo}", jsonRes)

    def tcpClose(self):
        # 关闭
        stop_thread(self.t2)
        stop_thread(self.t1)
        self.soketClient.close()


class PooledTcpFactory(PooledObjectFactory):

    # 创建实例
    def createInstance(self) -> PooledObject:
        return PooledObject(TcpClient())

    # 销毁实例
    def destroy(self, pooled_object: PooledObject) -> None:
        tcp: TcpClient = pooled_object.use()
        tcp.tcpClose()
        del pooled_object

    # 重置实例 归还时 会走这个逻辑
    def reset(self, pooled_object: PooledObject) -> PooledObject:
        # 无效时重置tcp
        if not self.validate(pooled_object):
            pooled_object = PooledObject(TcpClient())
        return pooled_object

    # 是否有效
    def validate(self, pooled_object: PooledObject) -> bool:
        tcp: TcpClient = pooled_object.use()
        return tcp.state


pond = Pond(
    # 单位为秒，借出对象的最长期限，超过期限的对象归还时会自动销毁
    borrowed_timeout=20,
    # 单位为秒，自动回收的间隔时间
    time_between_eviction_runs=300,
    # 守护线程,如果True，自动回收的线程会随主线程关闭而关闭
    thread_daemon=True,
    # 自动回收时的权重 使用次数小于 最大使用频次*权重会被自动回收
    eviction_weight=0.8
)

# 对象池 生成5个
tcpFactory = PooledTcpFactory(pooled_maxsize=5, least_one=True)
pond.register(tcpFactory, name="PooledTcpFactory")


# 借出tcp对象
def tcp_borrow():
    pooled_object: PooledObject = pond.borrow(name="PooledTcpFactory")
    return pooled_object


# 归还tcp对象
def tcp_recycle(pooled_object):
    pond.recycle(pooled_object, name="PooledTcpFactory")


def clearPool():
    pond.clear(name='PooledTcpFactory')
    pond.stop()
